home *** CD-ROM | disk | FTP | other *** search
/ Die Speccy' 97 / Die Speccy' 97.iso / amiga_system / the_aminet / comm / bbs / s342q07.lha / misc.c < prev    next >
C/C++ Source or Header  |  1995-09-12  |  52KB  |  2,014 lines

  1. /*
  2. *       misc.c
  3. *
  4. * Random functions.
  5. */
  6. /*
  7. *       history
  8. *
  9. * 86Aug19 HAW  Kill history because of space problems.
  10. * 84Jun10 JLS  Function changedate() installed.
  11. * 84May01 HAW  Starting 1.50a upgrade.
  12. * 83Mar12 CrT  from msg.c
  13. * 83Mar03 CrT & SB   Various bug fixes...
  14. * 83Feb27 CrT  Save private mail for sender as well as recipient.
  15. * 83Feb23 Various.  transmitFile() won't drop first char on WC...
  16. * 82Dec06 CrT  2.00 release.
  17. * 82Nov05 CrT  Stream retrieval.  Handles messages longer than MAXTEXT.
  18. * 82Nov04 CrT  Revised disk format implemented.
  19. * 82Nov03 CrT  Individual history begun.  General cleanup.
  20. */
  21. #include "ctdl.h"
  22. #define  TURBO_C_VSPRINTF_BUG
  23. /*
  24. *       contents
  25. *
  26. * ARCDir()    ARC TOC entries
  27. * calcrc()    calculates CRC
  28. * changeDate()      allow changing of date
  29. * CheckDLimit()     exceeded download time limit?
  30. * civTime()   MilTime to CivTime
  31. * CompressedDir()   manager of reading compressed dirs
  32. * configure()   sets terminal parameters via dialogue
  33. * crashout()    crashes out of Citadel in case of bug
  34. * doFormatted()   for wildCard
  35. * doCR()      newline on modem and console
  36. * download()    menu-level routine for WC-protocol sends
  37. * formRoom()    room prompt formatting
  38. * getCdate()    gets date from system clock.
  39. * GetSecond()   get seconds of minute, for multibanner
  40. * GifDir()    important data of a GIF file.
  41. * HelpIfPresent()   print help file if present
  42. * ingestFile()    puts file in held message buffer
  43. * lbyte()     finds 0 byte of a string
  44. * patchDebug()    display/patch byte
  45. * printDate()   prints out date
  46. * putBufChar()    .EWM/.EXM/.EWN/.EXN internal
  47. * putFLChar()   readFile() -> disk file interface
  48. * reconfigure()   Reconfigures a user
  49. * TranFiles()   Handles file transfers to users
  50. * TranSend()    Does send work of TranFiles()
  51. * transmitFile()    send a host file, no formatting
  52. * tutorial()    first level for printing a help file
  53. * upLoad()    menu-level read-via-WC-protocol fn
  54. * visible()   convert control chars to letters
  55. * writeTutorial()   prints a .hlp file
  56. * ZIPDir()    ZIP TOC entries
  57. */
  58.  
  59. static void Display_User_Configuration(void);
  60.  
  61.  
  62. extern int ClassActive[EVENT_CLASS_COUNT]; /* which classes are active? */
  63. char  *monthTab[13] =
  64.   {
  65.   "", "Jan", "Feb", "Mar",
  66.   "Apr", "May", "Jun",
  67.   "Jul", "Aug", "Sep",
  68.   "Oct", "Nov", "Dec"
  69.  
  70.   };
  71. FILE  *upfd;
  72. int masterCount;
  73. int acount;
  74. long byteRate; /* Bytes/sec that modem is set for.     */
  75. int DirAlign = 0;
  76. long  LowFree;
  77. char  AlignChar;
  78. char  *NoFileStr = "\n No %s.\n";
  79. char  *who_str = "who";
  80. /* char   *VERSION = "3.42.s6"; */
  81. char  *VERSION = VERSION_NAME;
  82. char  *ALL_LOCALS  = "&L";
  83. char  *R_SH_MARK   = "&&";
  84. char  *LOC_NET     = "++";
  85. char  *NON_LOC_NET = "%%";
  86. char  *WRITE_LOCALS = "All Local Systems";
  87. char  *APrivateRoom = "A Private Room";
  88. char  *LCHeld = "log%d.hld";
  89. char  FormatFlag = FALSE;
  90. long  Dl_Limit = -1l;
  91. long  *DL_Total;   /* Blech */
  92. extern SListBase MailForward;
  93. extern char FileTransStat;
  94. PROTO_TABLE Table[] =
  95.   {
  96.     {
  97.     "Ascii", 0, (IS_NUMEROUS | NEEDS_HDR), "ASCII", NULL, NULL, NULL,
  98.     outMod, 1, AsciiHeader, NULL
  99.  
  100.     }
  101.   ,
  102.     {
  103.     "Xmodem", 13, (RIGAMAROLE | IS_DL), "Xmodem", "Xmodem",
  104.     "wcdown.blb", "wcupload.blb", sendWCChar, SECTSIZE, NULL,
  105.     XYClear
  106.  
  107.     }
  108.   ,
  109.     {
  110.     "Ymodem", 11, (IS_NUMEROUS | RIGAMAROLE | IS_DL | NEEDS_FIN | NEEDS_HDR),
  111.     "Ymodem BATCH", "Ymodem SINGLE", "ymdown.blb", "ymodemup.blb",
  112.     sendYMChar, YM_BLOCK_SIZE, YMHdr, XYClear
  113.  
  114.     }
  115.   ,
  116.   #ifdef WXMODEM_AVAILABLE
  117.     {
  118.     "Wxmodem", 13, (RIGAMAROLE | IS_DL), "WXModem",
  119.     "WXModem", "wxdown.blb", "wxup.blb", sendWXModem, SECTSIZE,
  120.     NULL, ClearWX
  121.  
  122.     }
  123.   #else
  124.     {
  125.     NULL, 13, NOT_AVAILABLE, NULL, NULL, NULL, NULL, NULL, 0,
  126.     NULL, NULL
  127.  
  128.     }
  129.   #endif
  130.  
  131.   };
  132. int fixVers = 603;
  133. int majorVers = 114;
  134. char *netVersion = "1.16";
  135. #define AUDIT   9000
  136. char  audit[AUDIT];
  137. extern CONFIG    cfg;   /* Lots an lots of variables    */
  138. extern logBuffer   logBuf;    /* Person buffer    */
  139. extern logBuffer   logTmp;    /* Person buffer    */
  140. extern aRoom     roomBuf;   /* Room buffer    */
  141. extern rTable    *roomTab;
  142. extern MessageBuffer     msgBuf;
  143. extern MessageBuffer     tempMess;
  144. extern NetBuffer   netBuf;
  145. extern int     outPut;
  146. extern char    onConsole;
  147. extern AN_UNSIGNED crtColumn; /* where are we on screen now?  */
  148. extern char    loggedIn;  /* Is we logged in?     */
  149. extern char    outFlag;    /* Output flag     */
  150. extern char    haveCarrier;    /* Do we still got carrier?     */
  151. extern char    heldMess;
  152. extern int     TransProtocol;  /* transfer protocol in use     */
  153. extern char    prevChar;  /* previous char output   */
  154. extern char    textDownload;   /* flag     */
  155. extern int     thisRoom;
  156. extern int     thisLog;
  157. extern char    whichIO;    /* Where I/O is     */
  158. extern char    echo;     /* Should we echo? echo? echo?  */
  159. extern FILE    *msgfl;
  160. extern FILE    *roomfl;
  161. extern FILE    *logfl;
  162. extern int     exitValue;
  163. extern char    *LCHeld, *WRITE_ANY, *WRITE_TEXT;
  164. extern char    PrintBanner;
  165. FunnyInfo Formats[] =
  166.   {
  167.     {
  168.     "LHA", TRUE,  LZHDir
  169.  
  170.     }
  171.   ,
  172.     {
  173.     "LZH", TRUE,  LZHDir
  174.  
  175.     }
  176.   ,
  177.     {
  178.     "ZOO", TRUE,  ZOODir
  179.  
  180.     }
  181.   ,
  182.     {
  183.     "ARC", TRUE,  ARCDir
  184.  
  185.     }
  186.   ,
  187.     {
  188.     "GIF", FALSE, GifDir
  189.  
  190.     }
  191.   ,
  192.     {
  193.     "FRA", FALSE, GifDir
  194.  
  195.     }
  196.   ,
  197.     {
  198.     NULL, FALSE, NULL
  199.  
  200.     }
  201.   ,
  202.  
  203.   };
  204. /*
  205. * CompressType()
  206. *
  207. * This function finds the type of file the specified file is.
  208. */
  209. int CompressType(char *name)
  210.   {
  211.   int format;
  212.   char *c;
  213.   if ((c = strchr(name, '.')) != NULL)
  214.     {
  215.     for (format = 0; Formats[format].Format != NULL; format++)
  216.       {
  217.       if (strCmpU(c + 1, Formats[format].Format) == SAMESTRING)return format;
  218.  
  219.       };
  220.  
  221.     }
  222.   return -1;
  223.  
  224.   }
  225. /*
  226. * AsciiHeader()
  227. *
  228. * This will entitle an ASCII file transfer.
  229. */
  230. int AsciiHeader(long fileSize, char *filename)
  231.   {
  232.   char work[10];
  233.   doCR();
  234.   mPrintf("[ %s : %s bytes ]", filename, PrintPretty(fileSize, work));
  235.   doCR();
  236.   doCR();
  237.   return TRUE;
  238.  
  239.   }
  240. #define FN_LENGTH 90
  241. /*
  242. * CompressedDir()
  243. *
  244. * This function reads the TOC of compressed files using a table of function
  245. * pointers (for generic use) and displays it.
  246. */
  247. void CompressedDir(DirEntry *fn)
  248.   {
  249.   FILE  *fd;
  250.   char  FileName[FN_LENGTH];
  251.   char  DateStr[20];
  252.   long  RealSize, SmallSize;
  253.   int   count = 0;
  254.   long  compressed = 0l, realsize = 0l;
  255.   int   format;
  256.   extern char *READ_ANY;
  257.   extern int DirAlign;
  258.   extern char AlignChar;
  259.   if (outFlag != OUTOK) return;
  260.   if ((format = CompressType(fn->unambig)) == ERROR) return;
  261.   mPrintf("\n %s", fn->unambig);
  262.   if (FindFileComment(fn->unambig, FALSE))
  263.     {
  264.     DirAlign = strLen(fn->unambig) + 3;
  265.     AlignChar = 0;
  266.     mPrintf(":%s", strchr(msgBuf.mbtext, ' '));
  267.     DirAlign = 0;
  268.  
  269.     }
  270.   mPrintf("\n ");
  271.   if ((fd = fopen(fn->unambig, READ_ANY)) == NULL)
  272.     {
  273.     mPrintf("INTERNAL FILE ERROR!\n ");
  274.     return ;
  275.  
  276.     }
  277.   if (Formats[format].Many)
  278.     {
  279.     mPrintf("\n%7s   %8s%6s Name \n ", "Crunched", "Normal  ", " Date ");
  280.     while ((*Formats[format].Func)(fd, FileName, &RealSize, &SmallSize,
  281.     DateStr))
  282.       {
  283.       count++;
  284.       mPrintf("\n %7ld %8ld %6s %s", SmallSize, RealSize, DateStr,FileName);
  285.       compressed += SmallSize;
  286.       realsize   += RealSize;
  287.  
  288.       }
  289.     mPrintf("\n  ------- --------");
  290.     mPrintf("\n %7ld %8ld %d files\n ", compressed, realsize, count);
  291.  
  292.     }
  293.   else
  294.     {
  295.     (*Formats[format].Func)(fd, TRUE, msgBuf.mbtext);
  296.     mPrintf("%s", msgBuf.mbtext);
  297.  
  298.     }
  299.   fclose(fd);
  300.  
  301.   }
  302. /*
  303. * ARCDir()
  304. *
  305. * This function reads an ARC TOC entry and sets for the next one.
  306. */
  307. char ARCDir(FILE *fd, char *FileName, long *RSize, long *SSize, char *DateStr)
  308.   {
  309.   ARCbuf buf;
  310.   #ifndef IS_MOTOROLA
  311.   if (fread(&buf, sizeof buf, 1, fd) <= 0)
  312.   return FALSE;
  313.   #else
  314.   /* this mess is due to Lattice C doing structure padding on Amigas */
  315.   fread(&buf.ArchiveMark, 1, 1, fd);
  316.   fread(&buf.Header, 1, 1, fd);
  317.   fread(buf.name, 13, 1, fd);
  318.   fread(&buf.size, 4, 1, fd);
  319.   fread(&buf.date, 2, 1, fd);
  320.   fread(&buf.time, 2, 1, fd);
  321.   fread(&buf.crc, 2, 1, fd);
  322.   fread(&buf.length, 4, 1, fd);
  323.   #endif
  324.   if (buf.ArchiveMark != 0x1a || buf.Header == 0)
  325.   return FALSE;
  326.   strCpy(FileName, buf.name);
  327.   #ifdef IS_MOTOROLA
  328.   Intel32ToMotorola(&buf.size);
  329.   Intel32ToMotorola(&buf.length);
  330.   Intel16ToMotorola(&buf.date);
  331.   #endif
  332.   *SSize = buf.size;
  333.   *RSize = buf.length;
  334.   DosToNormal(DateStr, buf.date);
  335.   fseek(fd, buf.size, SEEK_CUR);
  336.   return TRUE;
  337.  
  338.   }
  339. /*
  340. * ZIPDir()
  341. *
  342. * This function reads a ZIP TOC entry and sets for the next one.
  343. */
  344. /*  ***NOT USED IN THE AMIGA VERSION
  345. char ZIPDir(FILE *fd, char *FileName, long *RSize, long *SSize, char *DateStr)
  346.   {
  347.   ZipHeader ZBuf;
  348.   if (fread(&ZBuf, sizeof ZBuf, 1, fd) < 1) return FALSE;
  349.   #ifdef IS_MOTOROLA
  350.   Intel32ToMotorola(&ZBuf.Signature);
  351.   Intel32ToMotorola(&ZBuf.CompSize);
  352.   Intel32ToMotorola(&ZBuf.NormalSize);
  353.   Intel16ToMotorola(&ZBuf.NameLength);
  354.   Intel16ToMotorola(&ZBuf.FieldLength);
  355.   Intel16ToMotorola(&ZBuf.FileDate);
  356.   #endif
  357.   if (ZBuf.Signature != 0x04034b50) return FALSE;
  358.   if (ZBuf.NameLength < 0 || ZBuf.NameLength > FN_LENGTH) return FALSE;
  359.   fread(FileName, ZBuf.NameLength, 1, fd);
  360.   FileName[ZBuf.NameLength] = 0;
  361.   fseek(fd, ZBuf.FieldLength + ZBuf.CompSize, 1);
  362.   *SSize = ZBuf.CompSize;
  363.   *RSize = ZBuf.NormalSize;
  364.   DosToNormal(DateStr, ZBuf.FileDate);
  365.   return TRUE;
  366.   }
  367.   ZIPDIR IS REMOVED */
  368.   /*
  369.   * GifDir()
  370.   *
  371.   * This reads the important data of a GIF file.
  372.   */
  373.   char GifDir(FILE *fd, char longexpl, char *sbuf)
  374.     {
  375.     GifHeader buf;
  376.     fread(&buf, sizeof buf, 1, fd);
  377.     #ifdef IS_MOTOROLA
  378.     Intel16ToMotorola(&buf.Width);
  379.     Intel16ToMotorola(&buf.Height);
  380.     #endif
  381.     sbuf[0] = 0;
  382.     if (longexpl)
  383.     sPrintf(sbuf, "File is %.6s, ", buf.Sig);
  384.     sPrintf(lbyte(sbuf), (longexpl) ? "%d X %d, %d colors.\n " :
  385.     "%3d X %3d X %2d", buf.Width, buf.Height,
  386.     1 << ((buf.Colors & 0x07) + 1));
  387.     return TRUE;
  388.  
  389.     }
  390.   /*
  391.   * ZOODir()
  392.   *
  393.   * This handles reading a Zoo entry.
  394.   */
  395.   char ZOODir(FILE *fd, char *FileName, long *RSize, long *SSize, char *DateStr)
  396.     {
  397.     zoo_header zh;
  398.     zoo_direntry de;
  399.     static char ZooStart = TRUE;
  400.     if (ZooStart)
  401.       {
  402.       ZooStart = FALSE;
  403.       fread(&zh, sizeof zh, 1, fd);
  404.       #ifdef IS_MOTOROLA
  405.       Intel32ToMotorola(&zh.zoo_start);
  406.       #endif
  407.       fseek(fd, zh.zoo_start, 0);
  408.  
  409.       }
  410.     do
  411.       {
  412.       if (fread(&de, sizeof de, 1, fd) < 1)
  413.         {
  414.         ZooStart = TRUE;
  415.         return FALSE;
  416.  
  417.         }
  418.       #ifdef IS_MOTOROLA
  419.       Intel32ToMotorola(&de.next);
  420.       #endif
  421.       fseek(fd, de.next, 0);
  422.  
  423.       }
  424.     while (de.deleted);
  425.     #ifdef IS_MOTOROLA
  426.     Intel32ToMotorola(&de.size_now);
  427.     Intel32ToMotorola(&de.org_size);
  428.     Intel16ToMotorola(&de.date);
  429.     #endif
  430.     *SSize = de.size_now;
  431.     *RSize = de.org_size;
  432.     strcpy(FileName, de.fname);
  433.     DosToNormal(DateStr, de.date);
  434.     return TRUE;
  435.  
  436.     }
  437.   /*
  438.   * LZHDir()
  439.   *
  440.   * This function handles reading a lzh entry.
  441.   *
  442.   * courtesy Daniel Durbin.
  443.   */
  444.   char LZHDir(FILE *fd, char *FileName, long *RSize, long *SSize, char *DateStr)
  445.     {
  446.     LZHead header;
  447.     #ifndef IS_MOTOROLA
  448.     if (fread(&header, sizeof(header), 1, fd) < 1)
  449.     return FALSE;
  450.     #else
  451.     if (fread(header.unknown1, sizeof header.unknown1, 1, fd) < 1)
  452.     return FALSE;
  453.     if (fread(header.method, sizeof header.method, 1, fd) < 1)
  454.     return FALSE;
  455.     if (fread(&header.csize, sizeof header.csize, 1, fd) < 1)
  456.     return FALSE;
  457.     if (fread(&header.fsize, sizeof header.fsize, 1, fd) < 1)
  458.     return FALSE;
  459.     if (fread(&header.ftime, sizeof header.ftime, 1, fd) < 1)
  460.     return FALSE;
  461.     if (fread(&header.fdate, sizeof header.fdate, 1, fd) < 1)
  462.     return FALSE;
  463.     if (fread(&header.fattr, sizeof header.fattr, 1, fd) < 1)
  464.     return FALSE;
  465.     if (fread(&header.unknown2, sizeof header.unknown2, 1, fd) < 1)
  466.     return FALSE;
  467.     if (fread(&header.namelen, sizeof header.namelen, 1, fd) < 1)
  468.     return FALSE;
  469.     #endif
  470.     if (header.namelen < 1 || header.namelen >= FN_LENGTH)
  471.     return FALSE;
  472.     if (fread(FileName, 1, header.namelen, fd) != header.namelen)
  473.     return FALSE;
  474.     FileName[header.namelen] = 0;
  475.     fgetc(fd), fgetc(fd); /* gets CRC I guess */
  476.     /* I don't* know what this is jumping over - but it works! */
  477.     if (header.unknown2 == 1)
  478.       {
  479.       fgetc(fd);
  480.       fgetc(fd);
  481.       fgetc(fd);
  482.  
  483.       }
  484.     #ifdef IS_MOTOROLA
  485.     Intel32ToMotorola(&header.fsize);
  486.     Intel32ToMotorola(&header.csize);
  487.     Intel16ToMotorola(&header.fdate);
  488.     #endif
  489.     *SSize = header.csize;
  490.     *RSize = header.fsize;
  491.     DosToNormal(DateStr, header.fdate);
  492.     fseek(fd, header.csize, SEEK_CUR);
  493.     return TRUE;
  494.  
  495.     }
  496.   /*
  497.   * DosToNormal()
  498.   *
  499.   * This function converts a DOS-formatted date to a formatted string.  This
  500.   * perhaps should reside in the system dependent code....
  501.   */
  502.   void DosToNormal(char *DateStr, UNS_16 DosDate)
  503.     {
  504.     if (((DosDate & 0x1e0) >> 5) > 12 ||
  505.     ((DosDate & 0x1e0) >> 5) < 1)
  506.     strCpy(DateStr, "No Date");
  507.     else
  508.     sprintf(DateStr, "%d%s%02d", ((DosDate & 0xfe00) >> 9) + 80,
  509.     monthTab[(DosDate & 0x1e0) >> 5], DosDate & 0x1f);
  510.  
  511.     }
  512.   /*
  513.   * calcrc()
  514.   *
  515.   * Calculates CRC for a given block.
  516.   */
  517.   CRC_TYPE calcrc(unsigned char *ptr, int count)
  518.     {
  519.     register CRC_TYPE checksum;
  520.     register int i;
  521.     checksum=0;
  522.     while (count--)
  523.       {
  524.       i=(checksum >> 8) & 0xff;
  525.       i ^= *ptr++;
  526.       i ^= i >> 4;
  527.       checksum <<= 8;
  528.       checksum ^= i;
  529.       i <<= 5;
  530.       checksum ^= i;
  531.       i <<= 7;
  532.       checksum ^= i;
  533.  
  534.       }
  535.     return(checksum);
  536.  
  537.     }
  538.   #ifdef NEED_AVAILABLE
  539.   /************************************************************************/
  540.   /*  changedate() gets the date from the aide and remembers it */
  541.   /************************************************************************/
  542.   void changeDate()
  543.     {
  544.     int year, day, hours, minutes, mon;
  545.     char *month;
  546.     Output_Citadel_Message("CHNGDT",NULL,NULL,NULL);
  547.     if (!getYesNo("ETDTMN"))
  548.     return ;
  549.     do
  550.       {
  551.       year    = (int) getNumber("YEARPR",  87l, 99l) + 1900;
  552.       mon     = (int) getNumber("MONTPR", 1l,  12l)     ;
  553.       day     = (int) getNumber("DAYPRM",   1l,  31l)     ;
  554.       hours   = (int) getNumber("HOURPR",   0l, 23l)     ;
  555.       minutes = (int) getNumber("MINTPR", 0l, 59l)     ;
  556.  
  557.       }
  558.     while (!setRawDate(year, mon, day, hours, minutes));
  559.     /*    InitEvents(); */
  560.  
  561.     }
  562.   #endif
  563.   /*
  564.   * CheckDLimit()
  565.   *
  566.   * This checks to see if the next d/l will exceed the the limit or if it'll
  567.   * interfere with a preemptive event.  It returns FALSE on interference,
  568.   * TRUE otherwise.
  569.   */
  570.   char CheckDLimit(long estimated)
  571.     {
  572.     char *problem;
  573.     extern char *DlMsgPtr;
  574.     if (!aide && Dl_Limit_On() &&
  575.     (*DL_Total) + estimated >= Dl_Limit * 60)
  576.       {
  577.       mPrintf("I'm sorry, that would exceed the current cumulative download time limit ");
  578.       if (strLen(DlMsgPtr) != 0)
  579.       mPrintf("of %s", DlMsgPtr);
  580.       mPrintf(" -- you've currently spent %ld:%02ld in downloading.\n ",
  581.       (*DL_Total) / 60l, (*DL_Total) % 60l);
  582.       return FALSE;
  583.  
  584.       }
  585.     if ((problem = ChkPreempt(estimated)) != NULL)
  586.       {
  587.       mPrintf("Sorry, that would interfere with %s.\n ", problem);
  588.       return FALSE;
  589.  
  590.       }
  591.     return TRUE;
  592.  
  593.     }
  594.   /*
  595.   * civTime()
  596.   *
  597.   * Military time to Civilian time.
  598.   */
  599.   void civTime(int *hours, char **which)
  600.     {
  601.     if (*hours >= 12)
  602.     *which = "pm";
  603.     else
  604.     *which = "am";
  605.     if (*hours >= 13)
  606.     *hours -= 12;
  607.     if (*hours == 0)
  608.     *hours = 12;
  609.  
  610.     }
  611.   /*
  612.   * configure()
  613.   *
  614.   * This sets up the terminal width etc via dialogue.
  615.   */
  616.   char configure(logBuffer *lBuf, char AllQuestions, char AllowAbort)
  617.     {
  618.     int width, xwidth;    /* really! ugly kludge -- fix someday */
  619.     lBuf->lbnulls   = 0;
  620.     logBuf.lbdelay  = 0;
  621.     width = termWidth;
  622.     do
  623.       {
  624.       /* this gross width stuff is caused by that #define in ctdl.h */
  625.       termWidth = width;
  626.       lBuf->lbwidth   = (int) getNumber("COLSCR", 0l, 132l);
  627.       if( !gotCarrier() )
  628.         {
  629.         haveCarrier      = FALSE;
  630.         };
  631.       xwidth = lBuf->lbwidth;
  632.       if (onLine() && lBuf->lbwidth == 0 && AllowAbort)
  633.         {
  634.         termWidth = width;
  635.         if (getYesNo("ABORTA")) return FALSE;
  636.  
  637.         }
  638.       if (lBuf->lbwidth < 40)
  639.         {
  640.         termWidth = width;
  641.         Output_Citadel_Message("WIDT40",NULL, NULL, NULL);
  642.         }
  643.       lBuf->lbwidth = xwidth;
  644.  
  645.       }
  646.     while ( onLine() && lBuf->lbwidth < 40 );
  647.     if (AllQuestions)
  648.       {
  649.       lBuf->lbflags.LFMASK = getYesNo("NEEDLF") ? TRUE : FALSE;
  650.  
  651.       }
  652.     else
  653.       {
  654.       Output_Citadel_Message("XXXXXX",NULL,NULL,NULL);
  655.       lBuf->lbflags.LFMASK = getYesNo("ISBLNK") ? FALSE : TRUE;
  656.  
  657.       }
  658.     lBuf->lbflags.EXPERT      = getYesNo("AREEXP") ? TRUE : FALSE;
  659.     if (lBuf->lbflags.EXPERT || AllQuestions)
  660.       {
  661.       lBuf->lbflags.TIME      = getYesNo("PRTTME") ? TRUE : FALSE;
  662.       lBuf->lbflags.OLDTOO    = getYesNo("PRTOLD") ? TRUE : FALSE;
  663.       lBuf->lbflags.FLOORS    = getYesNo("FLRMOD");
  664.       lBuf->lbflags.ANSI      = getYesNo("ANSIHD");
  665.       lBuf->lbflags.MSG_PAUSE = getYesNo("MSGPAS");
  666.       }
  667.     else
  668.       {
  669.       lBuf->lbflags.OLDTOO    = FALSE;
  670.       lBuf->lbflags.TIME      = TRUE;
  671.       lBuf->lbflags.FLOORS    = TRUE;
  672.       lBuf->lbflags.HALF_DUP  = FALSE;
  673.       lBuf->lbflags.ANSI      = FALSE;  /* No ANSI colors */
  674.       lBuf->lbflags.MSG_PAUSE = FALSE;  /* no pause       */
  675.       }
  676.     return TRUE;
  677.  
  678.     }
  679.   /*
  680.   * crashout()
  681.   *
  682.   * Problems?  Out we go!!! This is a general error exit function.
  683.   */
  684.   void crashout(char *message)
  685.     {
  686.     FILE *fd;       /* Record some crash data */
  687.     int  i;
  688.     exitValue = CRASH_EXIT;
  689.     outFlag = IMPERVIOUS;
  690.     Output_Citadel_Message("REDALT",(long)message,NULL,NULL);
  691.     HangUp(FALSE);
  692.     logMessage(L_OUT, "", 0);
  693.     logMessage(CRASH_OUT, "", 0);
  694.     fd = safeopen("crash", "w");
  695.     fprintf(fd, message);
  696.     fclose(fd);
  697.     fd = safeopen("audit", "w");
  698.     for (i = 0; i < AUDIT; i++)
  699.       {
  700.       fputc(audit[i], fd);
  701.       if ((i+1) % 70 == 0) fprintf(fd, "\n");
  702.  
  703.       }
  704.     fprintf(fd, "\n\ncounter = %d\n", acount);
  705.     fclose(fd);
  706.     writeSysTab();
  707.     ModemShutdown(TRUE);
  708.     systemShutdown(0);
  709.     exit(exitValue);
  710.  
  711.     }
  712.   /*
  713.   * doFormatted()
  714.   *
  715.   * This does a tutorial for a wildCard call.
  716.   */
  717.   void doFormatted(DirEntry *fn)
  718.     {
  719.     tutorial(fn->unambig, FALSE);
  720.  
  721.     }
  722.   /*
  723.   * doCR()
  724.   *
  725.   * This does a newline on modem and console.
  726.   */
  727.   void doCR()
  728.     {
  729.     int i;
  730.     crtColumn   = 1;
  731.     if (outFlag != OUTOK &&     /* output is being s(kip)ped    */
  732.     outFlag != IMPERVIOUS)
  733.     return;
  734.     if (outPut == DISK) fprintf(upfd, "\n");
  735.     else
  736.       {
  737.       if (TransProtocol == ASCII)
  738.       mputChar(NEWLINE);
  739.       if (haveCarrier)
  740.         {
  741.         (*Table[TransProtocol].method)('\r');
  742.         if (TransProtocol == ASCII)
  743.         for (i = termNulls;  i;  i--) outMod(0);
  744.         if (termLF)
  745.         (*Table[TransProtocol].method)('\n');
  746.  
  747.         }
  748.       /* Kludge alert!  Kludge alert! */
  749.       /* We don't have to check TransProtocol, though. */
  750.       if (DirAlign != 0 && termWidth > 22)
  751.         {
  752.         #ifndef TURBO_C_VSPRINTF_BUG
  753.         mPrintf("%*c%c ", DirAlign, ' ', AlignChar);
  754.         #else
  755.         /* SUPER YUCKY! */
  756.         crtColumn += DirAlign + 1;
  757.         for (i = 0; i < DirAlign; i++)
  758.           {
  759.           mputChar(' ');
  760.           if (haveCarrier)
  761.           (*Table[TransProtocol].method)(' ');
  762.  
  763.           }
  764.         mputChar(AlignChar);
  765.         mputChar(' ');
  766.         if (haveCarrier)
  767.           {
  768.           (*Table[TransProtocol].method)(AlignChar);
  769.           (*Table[TransProtocol].method)(' ');
  770.  
  771.           }
  772.         #endif
  773.  
  774.         }
  775.  
  776.       }
  777.     prevChar    = ' ';
  778.  
  779.     }
  780.   /*
  781.   * download()
  782.   *
  783.   * This is the is the menu-level send-message-via-protocol function.
  784.   */
  785.   void download(char whichMess, char revOrder, char protocol, char global,
  786.   int Compression)
  787.     {
  788.     char result;
  789.     int  count;
  790.     extern char *APPEND_TEXT, Showing;
  791.     char CompFile[30];
  792.     outFlag     = OUTOK;
  793.     if (Table[protocol].MsgTran != NULL)
  794.        Output_Citadel_Message("DLFUNC",
  795.                 (long)(InternalProtocol(protocol) ? Table[protocol].MsgTran :
  796.                 FindProtoName(protocol)), NULL, NULL);
  797.     if (Compression != NO_COMP)
  798.        Output_Citadel_Message("DLCMPR",(long)GetCompEnglish(Compression), NULL, NULL);
  799.     if (InternalProtocol(protocol) && !expert &&
  800.     Table[protocol].BlbName != NULL)
  801.     tutorial(Table[protocol].BlbName, TRUE);
  802.     if (protocol != ASCII)
  803.       {
  804.       if (!getYesNo("RDYBEG"))  return;
  805.  
  806.       }
  807.     if (!InternalProtocol(protocol) || Compression != NO_COMP)
  808.       {
  809.       result = TRAN_SUCCESS;
  810.       ToTempArea();
  811.       Output_Citadel_Message("DELAYM",NULL,NULL,NULL);
  812.       echo = NEITHER;
  813.       if (!redirect("msgs")) return;
  814.  
  815.       }
  816.     else
  817.       {
  818.       if (protocol != ASCII) echo = NEITHER;
  819.       result = Transmission(protocol, STARTUP);
  820.  
  821.       }
  822.     if (protocol != ASCII || Compression != NO_COMP) Showing = DL_MSGS;
  823.     if (result == TRAN_SUCCESS)
  824.       {
  825.       if (!global)
  826.         {
  827.         count = showMessages(whichMess, revOrder,
  828.         logBuf.lbvisit[logBuf.lbgen[thisRoom] & CALLMASK],
  829.         OptionValidate);
  830.         if (count == 0) Output_Citadel_Message("NONEWM",NULL,NULL,NULL);
  831.  
  832.         }
  833.       else
  834.       doGlobal(whichMess, revOrder);
  835.       if (!InternalProtocol(protocol) || Compression != NO_COMP)
  836.         {
  837.         undirect();
  838.         if (Compression != NO_COMP)
  839.           {
  840.           sPrintf(CompFile, "msgs.%s", CompExtension(Compression));
  841.           Compress(Compression, "msgs", CompFile);
  842.           unlink("msgs"); /* because we use TranSend to send files */
  843.           if (access(CompFile, 0) != 0)
  844.             {
  845.             mPrintf("Error: The compression failed.\n ");
  846.             KillTempArea();
  847.             return;
  848.  
  849.             }
  850.  
  851.           }
  852.         else strCpy(CompFile, "msgs");
  853.         TranSend(protocol, transmitFile, ALL_FILES, "", FALSE);
  854.         unlink("msgs");
  855.         unlink(CompFile);
  856.         KillTempArea();
  857.  
  858.         }
  859.       else Transmission(TransProtocol, FINISH);
  860.       Showing = WHATEVER;
  861.  
  862.       }
  863.     echo = BOTH;
  864.     TransProtocol = ASCII;
  865.     /*
  866.     * If we have a console timeout during message display (during a Pause,
  867.     * most likely), onLine() will not be true at this point.  But setUp()
  868.     * will blindly set it to TRUE, so we have to call this with some care.
  869.     */
  870.     if (onLine())
  871.     setUp(FALSE);
  872.  
  873.     }
  874.   int StartingRoom, CurRoom;
  875.   /*
  876.   * doGlobal()
  877.   *
  878.   * Does .R{Y,W,X,other protocols}G
  879.   */
  880.   void doGlobal(char whichMess, char revOrder)
  881.     {
  882.     extern char PhraseUser;
  883.     StartingRoom = CurRoom = thisRoom;
  884.     while (
  885.     ((whichMess == NEWoNLY && !PhraseUser) ? gotoRoom("", 'R') : NextSeq())
  886.     && (gotCarrier() || onConsole))
  887.       {
  888.       givePrompt();
  889.       mPrintf("read\n ");
  890.       showMessages(whichMess, revOrder,
  891.       logBuf.lbvisit[logBuf.lbgen[thisRoom] & CALLMASK],
  892.       OptionValidate);
  893.       doCR();   /* aesthetics, pig-dogs. */
  894.       if (outFlag == OUTSKIP) break;
  895.  
  896.       }
  897.  
  898.     }
  899.   /*
  900.   * NextSeq()
  901.   *
  902.   * This finds next room in sequence for doGlobal().
  903.   */
  904.   int NextSeq()
  905.     {
  906.     int i;
  907.     i = (CurRoom + 1) % MAXROOMS;
  908.     while (i != StartingRoom)
  909.       {
  910.       if (roomTab[i].rtflags.INUSE &&
  911.       KnownRoom(i) != UNKNOWN_ROOM)
  912.         {
  913.         getRoom(i);
  914.         CurRoom = i;
  915.         return TRUE;
  916.  
  917.         }
  918.       i = (i + 1) % MAXROOMS;
  919.  
  920.       }
  921.     return FALSE;
  922.  
  923.     }
  924.   /*
  925.   * formHeader()
  926.   *
  927.   * This returns a string with the msg header formatted.
  928.   */
  929. char *formHeader()
  930.     {
  931.     static char header[250];
  932.     header[0] = 0;      /* Initialize the genie.... */
  933.     if (msgBuf.mbdate[ 0])  sPrintf(lbyte(header), "  %s ", msgBuf.mbdate);
  934.     if (msgBuf.mbtime[ 0] && sendTime) sPrintf(lbyte(header), "%s ", msgBuf.mbtime);
  935.     if (msgBuf.mbauth[ 0]) sPrintf(lbyte(header), "from %s" ,msgBuf.mbauth );
  936.     NormStr(msgBuf.mboname);
  937.     if (msgBuf.mboname[0])
  938.       {
  939.       sPrintf(lbyte(header), " @ %s", msgBuf.mboname);
  940.       if (msgBuf.mbdomain[0])sPrintf(lbyte(header), cfg.DomainDisplay, msgBuf.mbdomain);
  941.       };
  942.     if (strCmpU(msgBuf.mbroom, roomBuf.rbname) != SAMESTRING)
  943.       {
  944.       strCat(header, " in ");
  945.       if (roomExists(msgBuf.mbroom) != ERROR)
  946.       sPrintf(lbyte(header), formRoom(roomExists(msgBuf.mbroom), FALSE,
  947.       FALSE));
  948.       else
  949.       sPrintf(lbyte(header), "%s>", msgBuf.mbroom);
  950.  
  951.       };
  952.     if (msgBuf.mbto[   0])
  953.       {
  954.       sPrintf(lbyte(header), " to %s" , msgBuf.mbto);
  955.       if (!msgBuf.mbauth[0] && thisRoom == MAILROOM &&
  956.       strLen(cfg.SysopName) != 0)     /* Mail to sysop */
  957.       sPrintf(lbyte(header), " (%s)", cfg.SysopName);
  958.  
  959.       };
  960.     if (msgBuf.mbaddr[ 0] &&
  961.     strncmp(msgBuf.mbaddr, R_SH_MARK, strLen(R_SH_MARK)) != SAMESTRING &&
  962.     strncmp(msgBuf.mbaddr, LOC_NET, strLen(LOC_NET)) != SAMESTRING &&
  963.     strncmp(msgBuf.mbaddr, NON_LOC_NET, strLen(NON_LOC_NET)) != SAMESTRING)
  964.     sPrintf(lbyte(header), " (on %s)", strCmpU(msgBuf.mbaddr, ALL_LOCALS) ?
  965.     msgBuf.mbaddr : "All Local Systems");
  966.     return header;
  967.  
  968.     }
  969.   /*
  970.   * formRoom()
  971.   *
  972.   * This returns a string with the room formatted, including the prompt type.
  973.   */
  974.   char display[40];
  975.   char matrix[2][2] =
  976.     {
  977.       { '>', ')'  },
  978.       { ']', ':'  }
  979.  
  980.     };
  981.  
  982. #define ANSII_NONE   ""       /* reset, ansii off */
  983. #define ANSII_RED    ""   /* RED    */
  984. #define ANSII_GREEN  ""   /* GREEN  */
  985. #define ANSII_ORANGE ""   /* ORANGE */
  986. #define ANSII_BLUE   ""   /* BLUE   */
  987.  
  988.  
  989. char *ccode[4] =
  990.     {
  991.     ANSII_ORANGE,  /* Orange: Regular, no directory   */
  992.     ANSII_RED,     /* RED:    Networked, no directory */
  993.     ANSII_GREEN,   /* GREEN:  Regular, Directory      */
  994.     ANSII_BLUE     /* BLUE:   Networked, Directory    */
  995.     };
  996.  
  997. char *formRoom(int roomNo, int showPriv, int noDiscrimination)
  998.     {
  999.     int   one, two, color;
  1000.     one = roomTab[roomNo].rtflags.ISDIR;
  1001.     two = (roomTab[roomNo].rtflags.SHARED && cfg.BoolFlags.netParticipant);
  1002.     if (roomTab[roomNo].rtflags.INUSE)
  1003.       {
  1004.       if (!noDiscrimination && !roomTab[roomNo].rtflags.PUBLIC)
  1005.         strcpy(display, APrivateRoom);
  1006.       else
  1007.         {
  1008.         if( logBuf.lbflags.ANSI )
  1009.           {
  1010.           color = one*2 + two;
  1011.           sPrintf(display, "%s%s%s%c%s"
  1012.           ,ccode[color], roomTab[roomNo].rtname,ANSII_NONE, matrix[one][two],
  1013.           (!roomTab[roomNo].rtflags.PUBLIC && showPriv) ? "*" : "");
  1014.           }
  1015.         else
  1016.           {
  1017.           sPrintf(display, "%s%c%s", roomTab[roomNo].rtname, matrix[one][two],
  1018.           (!roomTab[roomNo].rtflags.PUBLIC && showPriv) ? "*" : "");
  1019.           };
  1020.         };
  1021.       }
  1022.     else display[0] = '\0';
  1023.     return display;
  1024.  
  1025.     }
  1026.   /*
  1027.   * getCdate()
  1028.   *
  1029.   * This retrieves system date and returns in the parameters.
  1030.   */
  1031.   void getCdate(int *year, char **month, int *day, int *hours, int *minutes)
  1032.     {
  1033.     int mon, seconds, milli;
  1034.     getRawDate(year, &mon, day, hours, minutes, &seconds, &milli);
  1035.     *year -= 1900;
  1036.     *month = monthTab[mon];
  1037.  
  1038.     }
  1039.   /*
  1040.   * GetSecond()
  1041.   *
  1042.   * This will return the second of the minute.  For multibanner.
  1043.   */
  1044.   int GetSecond()
  1045.     {
  1046.     int y, d, h, m, seconds, ml, mon;
  1047.     getRawDate(&y, &mon, &d, &h, &m, &seconds, &ml);
  1048.     return seconds;
  1049.  
  1050.     }
  1051.   /*
  1052.   * HelpIfPresent()
  1053.   *
  1054.   * This will print help file if present, but not complain.
  1055.   */
  1056.   char HelpIfPresent(char *filename)
  1057.     {
  1058.     SYS_FILE fn;
  1059.     makeSysName(fn, filename, &cfg.homeArea);
  1060.     if (access(fn, 4) == 0)
  1061.       {
  1062.       tutorial(filename, TRUE);
  1063.       return TRUE;
  1064.  
  1065.       }
  1066.     else return FALSE;
  1067.  
  1068.     }
  1069.   /*
  1070.   * ingestFile()
  1071.   *
  1072.   * This puts the given file in the held msg buffer.
  1073.   */
  1074.   char ingestFile(char *name, MessageBuffer *msg)
  1075.     {
  1076.     char  filename[100];  /* Paths, etc.... */
  1077.     FILE  *fd;
  1078.     int   c, d, index;
  1079.     extern char *READ_TEXT;
  1080.     strCpy(filename, name);
  1081.     if ((fd = safeopen(filename, READ_TEXT)) == NULL)
  1082.       {
  1083.       return FALSE;
  1084.  
  1085.       }
  1086.     index = (heldMess) ? strLen(msg->mbtext) : 0;
  1087.     while ((c = fgetc(fd)) != EOF && index < MAXTEXT - 2)
  1088.       {
  1089.       if (c)
  1090.         {
  1091.         if (c == '\n')
  1092.           {
  1093.           /*
  1094.           * this should shave off trailing spaces.
  1095.           */
  1096.           while (index - 1 >= 0 && msg->mbtext[index - 1] == ' ')
  1097.           index--;
  1098.           while (!(d = fgetc(fd)))   /* skip any following zero bytes */
  1099.           ;
  1100.           if (d == '\n' || d == ' ' || d == EOF)
  1101.             {
  1102.             msg->mbtext[index++] = c;
  1103.             if (d != EOF)
  1104.             msg->mbtext[index++] = d;
  1105.  
  1106.             }
  1107.           else if (d)
  1108.             {
  1109.             msg->mbtext[index++] = ' ';
  1110.             msg->mbtext[index++] = d;
  1111.  
  1112.             }
  1113.  
  1114.           }
  1115.         else msg->mbtext[index++] = c;
  1116.  
  1117.         }
  1118.  
  1119.       }
  1120.     msg->mbtext[index] = 0;
  1121.     fclose(fd);
  1122.     CleanEnd(msg->mbtext);
  1123.     if (msg == &tempMess) heldMess = TRUE;
  1124.     return TRUE;
  1125.  
  1126.     }
  1127.   /*
  1128.   * formDate()
  1129.   *
  1130.   * This function forms the current date.
  1131.   */
  1132.   char *formDate()
  1133.     {
  1134.     static char dateLine[40];
  1135.     int  day, year, h, m;
  1136.     char *month;
  1137.     getCdate(&year, &month, &day, &h, &m);
  1138.     sPrintf(dateLine, "%d%s%02d", year, month, day);
  1139.     return dateLine;
  1140.  
  1141.     }
  1142.   /*
  1143.   * Current_Time()
  1144.   *
  1145.   * This function will get the current time, format cutely.
  1146.   */
  1147.   char *Current_Time()
  1148.     {
  1149.     char  *ml, *month;
  1150.     int   year, day, h, m;
  1151.     static char Time[13];
  1152.     getCdate(&year, &month, &day, &h, &m);
  1153.     civTime(&h, &ml);
  1154.     sPrintf(Time, "%d:%02d %s", h, m, ml);
  1155.     return Time;
  1156.  
  1157.     }
  1158.   /*
  1159.   * MultiBanner()
  1160.   *
  1161.   * This function handles the multibanner feature.
  1162.   */
  1163.   char MultiBanner(char *str)
  1164.     {
  1165.     SYS_FILE temp;
  1166.     makeBanner(temp, str, GetSecond());
  1167.     if (access(temp, 4) == 0)
  1168.       {
  1169.       tutorial(temp, FALSE);
  1170.       return TRUE;
  1171.  
  1172.       }
  1173.     else return FALSE;
  1174.  
  1175.     }
  1176.   /*
  1177.   * putBufChar()
  1178.   *
  1179.   * This is used to upload messages via protocol.
  1180.   * returns: ERROR on problems else TRUE.
  1181.   */
  1182.   int putBufChar(int c)
  1183.     {
  1184.     char result;
  1185.     if (masterCount == MAXTEXT + 10) return TRUE;
  1186.     if (masterCount > MAXTEXT - 2) return ERROR;
  1187.     /* This is necessary for a ProComm bug */
  1188.     if (c == CPMEOF)
  1189.       {
  1190.       masterCount = MAXTEXT + 10;
  1191.       return TRUE;
  1192.  
  1193.       }
  1194.     c &= 0x7F;          /* strip high bit */
  1195.     result = cfg.filter[c];
  1196.     if (result == '\0')
  1197.       {
  1198.       return TRUE;
  1199.  
  1200.       }
  1201.     msgBuf.mbtext[masterCount++] = result;
  1202.     msgBuf.mbtext[masterCount]   = 0;   /* EOL just for luck    */
  1203.     return TRUE;
  1204.  
  1205.     }
  1206.   /*
  1207.   * putFLChar()
  1208.   *
  1209.   * This is used to upload files.
  1210.   * returns: ERROR on problems else TRUE.
  1211.   */
  1212.   int putFLChar(int c)
  1213.     {
  1214.     extern FILE *netLog;
  1215.     if (fputc(c, upfd) != EOF)  return TRUE;
  1216.     /* else */      splitF(netLog, "Write error: %d\n", ferror(upfd));
  1217.     return ERROR;
  1218.  
  1219.     }
  1220.   /*
  1221.   * reconfigure()
  1222.   *
  1223.   * This function reconfigures a user, depending on their selection on the
  1224.   * dot command.
  1225.   *
  1226.   * Note: returns TRUE on backspace, FALSE otherwise
  1227.   */
  1228.   char reconfigure()
  1229.     {
  1230.     extern char MeetDisabled;
  1231.     char  *ON  = "ON", *OFF = "OFF";
  1232.     label alias, domain;
  1233.     char  system[(2 * NAMESIZE) + 10];
  1234.     int cost;
  1235.     extern int thisNet;
  1236.     char *ConfgOpts[] =
  1237.       {
  1238.       "C(omplete Reconfigure)  ", "E(xpert)          ", "F(loor mode)\n",
  1239.       "H(alf-duplex mode)      ", "L(inefeeds)       ", "N(ulls)\n",
  1240.       "O(ld messsage on new)   ", "T(ime of messages)", "D(elay)\n",
  1241.       "W(idth in columns)      ", "\r",                 "\n",
  1242.       "Z(Old .RE)              ", "G(raphics)        ", "\b",
  1243.       "Mail Forwarding         ", "Y(ield after msg)   ",
  1244.       "Prompt (message entry)\n",
  1245.       " ", " ", ""
  1246.  
  1247.       };
  1248.     static char *NOW = "Now %s.";
  1249.     RegisterThisMenu("confg.mnu", ConfgOpts);
  1250.     if (cfg.BoolFlags.netParticipant)
  1251.     ExtraOption(ConfgOpts, "Address\n");
  1252.     if (!MeetDisabled && loggedIn)
  1253.       {
  1254.       ExtraOption(ConfgOpts, "Biography");
  1255.  
  1256.       }
  1257.     switch (GetMenuChar())
  1258.       {
  1259.       case '\b': mPrintf(" \b"); PushBack('\b'); return TRUE;
  1260.       case 'A':     /* Forwarding address on the network */
  1261.       if (!ReqNodeName("FWRDML", alias, domain, FALSE,
  1262.       FALSE, TRUE, FALSE, FALSE, &netBuf) &&
  1263.       onLine())
  1264.         {
  1265.         /* in case carrier is lost */
  1266.         if (SearchList(&MailForward, logBuf.lbname) == NULL)
  1267.         break;
  1268.         else if (getYesNo("STOPFM"))
  1269.           {
  1270.           KillData(&MailForward, logBuf.lbname);
  1271.           UpdateForwarding();
  1272.           break;
  1273.  
  1274.           }
  1275.  
  1276.         }
  1277.       else if (onLine())
  1278.         {
  1279.         /* in case carrier is lost */
  1280.         sPrintf(system, (strLen(domain) != 0) ? "%s _ %s" : "%s%s",
  1281.         alias, domain);
  1282.         if (strLen(domain) != 0) cost = FindCost(domain);
  1283.         else cost = !netBuf.nbflags.local;
  1284.         if (cost > logBuf.credit)
  1285.           Output_Citadel_Message("WARNCR",(long)system,NULL,NULL);
  1286.         getString("ALIASF", alias, NAMESIZE, 0);
  1287.         if (onLine())
  1288.           {
  1289.           /* i.e., didn't drop carrier */
  1290.           AddMailForward(logBuf.lbname, system,
  1291.           (strLen(alias) != 0) ? alias : logBuf.lbname);
  1292.           if (!logBuf.lbflags.NET_PRIVS)
  1293.             Output_Citadel_Message("NETPRI",NULL, NULL, NULL);
  1294.  
  1295.           }
  1296.  
  1297.         }
  1298.       break;
  1299.       case 'M':
  1300.       getString("ALIASM", alias, NAMESIZE, 0);
  1301.       if (strLen(alias) != 0)
  1302.         {
  1303.         if (findPerson(alias, &logTmp) == ERROR)
  1304.             Output_Citadel_Message("NOPERS",(long)alias, NULL, NULL);
  1305.         else if (strCmpU(logTmp.lbname, logBuf.lbname) == SAMESTRING)
  1306.             Output_Citadel_Message("YRNAME",NULL, NULL, NULL);
  1307.         else
  1308.         AddMailForward(logBuf.lbname, NULL, alias);
  1309.  
  1310.         }
  1311.       else if (getYesNo("STOPFM"))
  1312.         {
  1313.         KillLocalFwd(logBuf.lbname);
  1314.  
  1315.         }
  1316.       break;
  1317.       case 'B':
  1318.       EditBio();
  1319.       break;
  1320.       case 'C':
  1321.       configure(&logBuf, FALSE, FALSE);
  1322.       break;
  1323.       case 'D':
  1324.       logBuf.lbdelay   = (int) getNumber("MDELAY", 0l, 255l);
  1325.       break;
  1326.       case 'G':
  1327.       logBuf.lbflags.ANSI = getYesNo("ANSIHD");
  1328.       break;
  1329.       case 'Y':
  1330.       logBuf.lbflags.MSG_PAUSE = getYesNo("MSGPAS");
  1331.       break;
  1332.       case 'E':
  1333.       mPrintf(NOW, (expert = !expert) ? ON : OFF);
  1334.       break;
  1335.       case 'P':
  1336.       mPrintf(NOW,(logBuf.lbflags.NoPrompt=!logBuf.lbflags.NoPrompt) ? OFF : ON);
  1337.       break;
  1338.       case 'Z':
  1339.       mPrintf(NOW, (logBuf.lbflags.ALT_RE = !logBuf.lbflags.ALT_RE) ? ON : OFF);
  1340.       break;
  1341.       case 'F':
  1342.       mPrintf(NOW, (FloorMode = !FloorMode) ? ON : OFF);
  1343.       break;
  1344.       case 'H':
  1345.       mPrintf(NOW, (HalfDup = !HalfDup) ? ON : OFF);
  1346.       break;
  1347.       case 'L':
  1348.       mPrintf(NOW, (termLF = !termLF) ? ON : OFF);
  1349.       break;
  1350.       case 'N':
  1351.       termNulls   = (int) getNumber("NULLSN", 0l, 255l);
  1352.       break;
  1353.       case 'O':
  1354.       mPrintf(NOW, (oldToo = !oldToo) ? ON : OFF);
  1355.       break;
  1356.       case 'T':
  1357.       mPrintf(NOW, (sendTime = !sendTime) ? ON : OFF);
  1358.       break;
  1359.       case 'W':
  1360.       termWidth   = (int) getNumber("COLSCRNO", 40l, 132l);
  1361.       break;
  1362.       case '\r':
  1363.       case '\n':
  1364.         tutorial("confg.mnu", TRUE);
  1365.       case '?':
  1366.         Display_User_Configuration();
  1367.       break;
  1368.  
  1369.       }
  1370.     storeLog();         /* save the current log entry!*/
  1371.     return FALSE;
  1372.  
  1373.     }
  1374.  
  1375. static void Display_User_Configuration()
  1376.   {
  1377.   ForwardMail *address;
  1378.   mPrintf("\n Current user setup:");
  1379.   mPrintf("\n     User Status: %10s           ANSI Headers: %3s",
  1380.   (expert) ? "Expert" : "Novice", (logBuf.lbflags.ANSI) ? "Yes" : "No");
  1381.   mPrintf("\n     Floor Mode : %10s           Use Alt .RE : %3s",
  1382.   (FloorMode) ? "On" : "Off",   (logBuf.lbflags.ALT_RE) ? "Yes" : "No");
  1383.   mPrintf("\n Linefeed Needed: %10s           Nulls       : %3d",
  1384.   (termLF) ? "Yes" : "No",  termNulls);
  1385.   mPrintf("\n Print msg time : %10s           Screen width: %3d",
  1386.   (sendTime) ? "Yes" : "No", termWidth);
  1387.   mPrintf("\n Last old msg   : %10s           Half-Duplex : %3s",
  1388.   (oldToo) ? "Yes" : "No", (HalfDup) ? "Yes" : "No");
  1389.   mPrintf("\n Char. delay    : %10d           Room Prompt : %3s",
  1390.   logBuf.lbdelay, (logBuf.lbflags.NoPrompt) ? "No" : "Yes");
  1391.   mPrintf("\n Message Pause  : %10s", (logBuf.lbflags.MSG_PAUSE) ? "Yes": "No");
  1392.   if (cfg.BoolFlags.netParticipant &&
  1393.      (address = SearchList(&MailForward, logBuf.lbname)) != NULL)
  1394.     {
  1395.     mPrintf("\n Forward Mail> to %s @%s.", address->Alias,address->System);
  1396.     };
  1397.   if (FindLocalForward(logBuf.lbname) != NULL)
  1398.     {
  1399.     mPrintf("\n Forward Mail> to local account %s.",
  1400.       FindLocalForward(logBuf.lbname));
  1401.     };
  1402.   mPrintf("\n ");
  1403.   }
  1404.   /*
  1405.   * SaveInterrupted()
  1406.   *
  1407.   * This saves an interrupted message.
  1408.   */
  1409.   void SaveInterrupted(MessageBuffer *SomeMsg)
  1410.     {
  1411.     SYS_FILE temp;
  1412.     SYS_FILE save_mess;
  1413.     extern char CCOutFlag;
  1414.     if (cfg.BoolFlags.HoldOnLost)
  1415.       {
  1416.       sPrintf(temp, LCHeld, thisLog);
  1417.       makeSysName(save_mess, temp, &cfg.holdArea);
  1418.       if (access(save_mess, 0) == -1)
  1419.         {
  1420.         if ((upfd = safeopen(save_mess, WRITE_ANY)) == NULL)
  1421.         printf("Failed to open save file!\n");
  1422.         else
  1423.           {
  1424.           crypte(SomeMsg, STATIC_MSG_SIZE, thisLog);
  1425.           fwrite(SomeMsg, STATIC_MSG_SIZE, 1, upfd);
  1426.           crypte(SomeMsg->mbtext, MAXTEXT, thisLog);
  1427.           fwrite(SomeMsg->mbtext, MAXTEXT, 1, upfd);
  1428.           CCOutFlag = TEXTFILE;
  1429.           RunList(&SomeMsg->mbCC, DisplayCC);
  1430.           fclose(upfd);
  1431.  
  1432.           }
  1433.  
  1434.         }
  1435.  
  1436.       }
  1437.  
  1438.     }
  1439.   /*
  1440.   * TranFiles()
  1441.   *
  1442.   * This handles transfer of files to users:
  1443.   *
  1444.   * 1. Gets number of files, number of bytes.
  1445.   * 2. Performs time calculations.
  1446.   * 3. Starts up protocols.
  1447.   */
  1448.   void TranFiles(int protocol, char *phrase)
  1449.     {
  1450.     extern unsigned long netBytes;
  1451.     int NumFiles;
  1452.     char FileSpec[100];
  1453.     getNormStr("EFILEN", FileSpec, sizeof FileSpec, 0);
  1454.     if (strLen(FileSpec) == 0) return;
  1455.     netBytes = 0l;
  1456.     NumFiles = wildCard(getSize, FileSpec, TRUE, phrase, TRUE);
  1457.     if (NumFiles <= 0l)
  1458.       {
  1459.       mPrintf("Sorry, no match for '%s'.\n ", FileSpec);
  1460.       return;
  1461.  
  1462.       }
  1463.     if (!TranAdmin(protocol, NumFiles)) return;
  1464.     TranSend(protocol, transmitFile, FileSpec, phrase, TRUE);
  1465.  
  1466.     }
  1467.   /*
  1468.   * TranAdmin()
  1469.   *
  1470.   * Transfer file administrator.
  1471.   */
  1472.   char TranAdmin(int protocol, int NumFiles)
  1473.     {
  1474.     long seconds;
  1475.     extern unsigned long netBytes;
  1476.     int s;
  1477.     if (NumFiles != 1 && ((InternalProtocol(protocol) &&
  1478.     !(Table[protocol].flags & IS_NUMEROUS)) ||
  1479.     (!InternalProtocol(protocol) && !DoesNumerous(protocol))))
  1480.       {
  1481.       mPrintf("%s does not support batch mode.\n ",
  1482.       (InternalProtocol(protocol)) ?
  1483.       Table[protocol].name : FindProtoName(protocol));
  1484.       return FALSE;
  1485.  
  1486.       }
  1487.     if (!InternalProtocol(protocol) || Table[protocol].flags & RIGAMAROLE)
  1488.       {
  1489.       mPrintf("This %s transfer involves %s bytes (",
  1490.       (!InternalProtocol(protocol)) ? FindProtoName(protocol) :
  1491.       Table[protocol].name,
  1492.       PrintPretty(netBytes, msgBuf.mbtext));
  1493.       mPrintf("%d file%s", NumFiles, NumFiles == 1 ? "" : "s");
  1494.       if (InternalProtocol(protocol))
  1495.       mPrintf(" : %ld blocks",
  1496.       ((netBytes+(Table[protocol].BlockSize-1))/Table[protocol].BlockSize));
  1497.       mPrintf(").  ");
  1498.       s = (!InternalProtocol(protocol)) ? 1 : protocol;
  1499.       if (Table[s].KludgeFactor != 0 && byteRate != 0)
  1500.         {
  1501.         seconds = (long) Table[s].KludgeFactor * netBytes /
  1502.         (byteRate * 10);
  1503.         if (!CheckDLimit(seconds)) return FALSE;
  1504.         mPrintf("It should take %ld:%02ld.\n ", seconds/60, seconds % 60);
  1505.  
  1506.         }
  1507.       return getYesNo("RDYBEG");
  1508.  
  1509.       }
  1510.     return TRUE;
  1511.  
  1512.     }
  1513. /*
  1514.  * TranSend()
  1515.  *
  1516.  * This does the send work of TranFiles().
  1517.  */
  1518. void TranSend(int protocol, void (*fn)(DirEntry *f), char *FileSpec,
  1519.                                                  char *phrase, char NeedToMove)
  1520. {
  1521.     char success;
  1522.     DirEntry temp = { "", "", 0l };
  1523.     SpecialMessage("Status:File Transfer");
  1524.  
  1525.     startTimer(WORK_TIMER);
  1526.  
  1527.     if (InternalProtocol(protocol)) {
  1528.         TransProtocol = protocol;
  1529.  
  1530.         wildCard((FormatFlag) ? doFormatted : fn, FileSpec, NeedToMove,
  1531.                                         phrase, TRUE);
  1532.  
  1533.         if (Table[protocol].flags & NEEDS_FIN)
  1534.             (*fn)(&temp);
  1535.     }
  1536.     else {
  1537.         success = ExternalProtocol(protocol, FALSE, FileSpec, phrase, NeedToMove);
  1538.         if (success == TRAN_SUCCESS)
  1539.             FileTransStat = FL_SUCCESS;
  1540.         else
  1541.             FileTransStat = FL_FAIL;
  1542.     }
  1543.  
  1544.     if (!InternalProtocol(protocol) || (Table[protocol].flags & IS_DL))
  1545.         *DL_Total += chkTimeSince(WORK_TIMER);
  1546.  
  1547.     TransProtocol = ASCII;
  1548.     if (!InternalProtocol(protocol) || (Table[protocol].flags & RIGAMAROLE))
  1549.         oChar(BELL);
  1550. }
  1551.  
  1552.  
  1553.   /*
  1554.   * transmitFile()
  1555.   *
  1556.   * This dumps a host file with no formatting.
  1557.   */
  1558.   void transmitFile(DirEntry *file)
  1559.     {
  1560.     FILE *fbuf;
  1561.     long fileSize = 0l;
  1562.     char *filename, success;
  1563.     extern char *READ_ANY;
  1564.     filename = file->unambig;
  1565.     if (strLen(filename) != 0)
  1566.       {
  1567.       if ((fbuf = safeopen(filename, READ_ANY)) == NULL)
  1568.         {
  1569.         return ;
  1570.  
  1571.         }
  1572.       totalBytes(&fileSize, fbuf);
  1573.       if (Table[TransProtocol].flags & RIGAMAROLE)
  1574.       printf("%s: %s (%ld bytes, %ld blocks)\n",
  1575.       Table[TransProtocol].name, filename, fileSize,
  1576.       ((fileSize+(Table[TransProtocol].BlockSize-1))/Table[TransProtocol].BlockSize));
  1577.       fileMessage(FL_START, filename, TRUE, TransProtocol, 0l);
  1578.  
  1579.       }
  1580.     if (Transmission(TransProtocol, STARTUP) != TRAN_SUCCESS)
  1581.       {
  1582.       fclose(fbuf);
  1583.       if (strLen(filename) != 0)
  1584.       fileMessage(FL_FAIL, filename, TRUE, TransProtocol, fileSize);
  1585.       return ;
  1586.  
  1587.       }
  1588.     if (Table[TransProtocol].flags & NEEDS_HDR)
  1589.       {
  1590.       if (!(*Table[TransProtocol].SendHdr)(fileSize, filename))
  1591.         {
  1592.         fclose(fbuf);
  1593.         if (strLen(filename) != 0)
  1594.         fileMessage(FL_FAIL, filename, TRUE, TransProtocol, fileSize);
  1595.         return ;
  1596.  
  1597.         }
  1598.  
  1599.       }
  1600.     if (strLen(filename) != 0)
  1601.       {
  1602.       SendThatDamnFile(fbuf, Table[TransProtocol].method);
  1603.       success = (Transmission(TransProtocol, FINISH) != TRAN_SUCCESS) ?
  1604.       FL_FAIL : FL_SUCCESS;
  1605.       fileMessage(success, filename, TRUE, TransProtocol, fileSize);
  1606.       if (TransProtocol == ASCII && outFlag != OUTOK)
  1607.         {
  1608.         doCR();
  1609.         outFlag = OUTOK;
  1610.  
  1611.         }
  1612.  
  1613.       }
  1614.  
  1615.     }
  1616.   /*
  1617.   * SendThatDamnFile()
  1618.   *
  1619.   * This will actually send the file.
  1620.   */
  1621.   void SendThatDamnFile(FILE *fbuf, int (*method)(int c))
  1622.     {
  1623.     int c, delay_factor;
  1624.     #ifdef AMIGA
  1625.     int oldc = 0;
  1626.     #endif
  1627.     if( loggedIn )
  1628.       {
  1629.       if( !onConsole && logBuf.lbdelay > 0 )
  1630.         {
  1631.         delay_factor = ( 256 - logBuf.lbdelay );
  1632.         };
  1633.       };
  1634.     while ((c = fgetc(fbuf)) != EOF && (c != CPMEOF || !textDownload))
  1635.       {
  1636.       if (TransProtocol == ASCII) mputChar(c);
  1637.       if (gotCarrier()) if (!(*method)(c))break;
  1638.       if (TransProtocol == ASCII)
  1639.         {
  1640.         if( loggedIn )
  1641.           {
  1642.           if (!onConsole && logBuf.lbdelay > 0 )
  1643.             {
  1644.             if( delay_factor-- <= 0 )
  1645.               {
  1646.               delay_factor = ( 256 - logBuf.lbdelay );
  1647.               MilliSecPause(3);
  1648.               };
  1649.             };
  1650.           };
  1651.         if (mAbort() || (whichIO == MODEM && !gotCarrier())) break;
  1652.         #ifdef AMIGA
  1653.         /* make handling text files more sane for non-Amiga people */
  1654.         if (gotCarrier() && c == '\n' && oldc != '\r')(*method)('\r');
  1655.         #endif
  1656.  
  1657.         }
  1658.       #ifdef AMIGA
  1659.       oldc = c;
  1660.       #endif
  1661.  
  1662.       }
  1663.     fclose(fbuf);
  1664.     textDownload = FALSE;
  1665.  
  1666.     }
  1667.   /*
  1668.   * tutorial()
  1669.   *
  1670.   * This prints file <filename> on the modem & console.
  1671.   * Returns:  TRUE on success else ERROR.
  1672.   */
  1673.   char tutorial(char *filename, char addHelpArea)
  1674.     {
  1675.     /* This includes writeTutorial */
  1676.     FILE     *fbuf;
  1677.     SYS_FILE fn;
  1678.     char     line[MAXWORD];
  1679.     extern char *READ_TEXT;
  1680.     if (addHelpArea)
  1681.     makeSysName(fn, filename, &cfg.homeArea);
  1682.     else
  1683.     strCpy(fn, filename);
  1684.     if ((fbuf = safeopen(fn, READ_TEXT)) == NULL)
  1685.       {
  1686.       mPrintf(NoFileStr, filename);
  1687.       return ERROR;
  1688.  
  1689.       }
  1690.     if (outFlag != IMPERVIOUS) outFlag     = OUTOK;
  1691.     Output_Citadel_Message("HOTHLP", NULL, NULL, NULL);
  1692.     while (fgets(line, MAXWORD, fbuf) && outFlag != OUTSKIP)
  1693.     mPrintf("%s", line);
  1694.     if (!PrintBanner) outFlag     = OUTOK;
  1695.     fclose(fbuf);
  1696.     return TRUE;    /* good as anything */
  1697.  
  1698.     }
  1699.  
  1700. /*
  1701.  * upLoad()
  1702.  *
  1703.  * This enters a file into current directory.
  1704.  */
  1705. void upLoad(int WC, char *file, char NeedToMove)
  1706. {
  1707.     char fileName[MAX_FILENAME - 1];
  1708.     char curdir[100];
  1709.     char successful;
  1710.     long size;
  1711.  
  1712.     getcwd(curdir, 100);
  1713.     if (file)
  1714.         strcpy(fileName, file);
  1715.     else
  1716.         getNormStr("EFILEN", fileName, sizeof fileName, 0);
  1717.     if (!fileName[0]) return;
  1718.  
  1719.         /* Can't tolerate bad file names */
  1720.     if (!ValidDirFileName(fileName)) {
  1721.         mPrintf("Illegal file name.\n ");
  1722.         return ;
  1723.     }
  1724.  
  1725.     if (NeedToMove) {
  1726.             if (!setSpace(&roomBuf)) {          /* System error -- yucky. */
  1727.                 return ;
  1728.         }
  1729.     }
  1730.  
  1731.     if (LowFree != 0 && RoomLeft(&roomBuf) < LowFree) {
  1732.         mPrintf("Sorry, not enough room left on disk.\n ");
  1733.         if (NeedToMove)
  1734.             homeSpace();
  1735.         return ;
  1736.     }
  1737.  
  1738.     if (access(fileName, 0) != -1) {
  1739.                                     /* File already exists */
  1740.         mPrintf("\n File: %s already exists.\n ", fileName);
  1741.         if (NeedToMove)
  1742.             homeSpace();
  1743.         return;
  1744.     } else {                    /* Go for it */
  1745.         if (!expert && InternalProtocol(WC)) {
  1746.             homeSpace();
  1747.             tutorial(Table[WC].UpBlbName, TRUE);
  1748.             if (!NeedToMove)
  1749.                 chdir(curdir);
  1750.             else {
  1751.       /****
  1752.                 if (!aide && roomBuf.rbflags.REDIRECT_UPLOADS
  1753.                     && cfg.newupArea.naDirname[0]) {
  1754.                     netSetNewArea(&cfg.newupArea);
  1755.                 }
  1756.                 else
  1757.       *****/
  1758.                     setSpace(&roomBuf);
  1759.             }
  1760.         }
  1761.  
  1762.         if (!getYesNo("RDYBEG")) {
  1763.             if (NeedToMove)
  1764.                 homeSpace();
  1765.             return;
  1766.         }
  1767.  
  1768.         if (!InternalProtocol(WC)) {
  1769.             fileMessage(FL_START, fileName, FALSE, WC, 0l);
  1770.             successful = (ExternalProtocol(WC, TRUE, fileName, NULL, NeedToMove)
  1771.                                                 == TRAN_SUCCESS);
  1772.             if (successful) {       /* so we can get the file size */
  1773.                 upfd = safeopen(fileName, READ_ANY);
  1774.             }
  1775.         }
  1776.         else {
  1777.             if ((upfd = safeopen(fileName, WRITE_ANY)) == NULL) {
  1778.                 mPrintf("\n Can't create %s!\n", fileName);
  1779.                 if (NeedToMove)
  1780.                     homeSpace();
  1781.                 return;
  1782.             }
  1783. #ifdef HORRID_AMIGA_LATTICE_BUG
  1784.             setnbf(upfd);
  1785. #endif
  1786.             fileMessage(FL_START, fileName, FALSE, WC, 0l);
  1787.             successful = (Reception(WC, putFLChar) == TRAN_SUCCESS);
  1788.         }
  1789.  
  1790.         if (successful) totalBytes(&size, upfd);
  1791.         fclose(upfd);
  1792.  
  1793.         if (!successful) unlink(fileName);
  1794.         else if ((successful = FileIntegrity(fileName))==0) {
  1795.             unlink(fileName);
  1796.         }
  1797.  
  1798.         if (NeedToMove)
  1799.             homeSpace();
  1800.  
  1801.         fileMessage(successful ? FL_SUCCESS : FL_FAIL, fileName,
  1802.                                         FALSE, WC, size);
  1803.         if (successful && fileName[0] != '*' && NeedToMove)  {
  1804.   /***          if (loggedIn)
  1805.                 upldcnt++;  ***/
  1806.             FileCommentUpdate(fileName, TRUE);
  1807.         }
  1808.     }
  1809. }
  1810.  
  1811.  
  1812.   /*
  1813.   * FileCommentUpdate()
  1814.   *
  1815.   * This updates the file comment.
  1816.   */
  1817.   void FileCommentUpdate(char *fileName, char aideMsg)
  1818.     {
  1819.     char *tmp;
  1820.     /* MsgEntryType = FILE_ENTRY; */
  1821.     do
  1822.       {
  1823.       msgBuf.mbtext[0] = 0;
  1824.       mPrintf("Please enter a description of %s (end with blank line).\n ", fileName);
  1825.       doCR();
  1826.  
  1827.       }
  1828.     while (onLine() && !GetBalance(ASCII, msgBuf.mbtext, MAXTEXT-50));
  1829.     if (onLine())
  1830.       {
  1831.       setSpace(&roomBuf); /* update file comments */
  1832.       while ((tmp = strchr(msgBuf.mbtext, '\r')) != NULL)
  1833.       *tmp = ' ';
  1834.       while ((tmp = strchr(msgBuf.mbtext, '\n')) != NULL)
  1835.       *tmp = ' ';
  1836.       if (aideMsg || strLen(msgBuf.mbtext) != 0)
  1837.         {
  1838.         if (loggedIn && !roomBuf.rbflags.ANON)
  1839.         sPrintf(lbyte(msgBuf.mbtext), " [%s].", logBuf.lbname);
  1840.         updFiletag(fileName, msgBuf.mbtext);
  1841.  
  1842.         }
  1843.       homeSpace();
  1844.       if (aideMsg || strLen(msgBuf.mbtext) != 0)
  1845.         {
  1846.         tmp = strdup(msgBuf.mbtext);
  1847.         ZeroMsgBuffer(&msgBuf);
  1848.         sPrintf(msgBuf.mbtext, "File \"%s\" uploaded into %s.",
  1849.         fileName, formRoom(thisRoom, FALSE, FALSE));
  1850.         if (loggedIn)
  1851.         sPrintf(lbyte(msgBuf.mbtext) - 1, " by %s.", logBuf.lbname);
  1852.         if (aideMsg) aideMessage(NULL,FALSE);
  1853.         sPrintf(msgBuf.mbtext, "File \"%s\" uploaded into %s:\n \n%s",
  1854.         fileName, formRoom(thisRoom, FALSE, FALSE), tmp);
  1855.         strCpy(msgBuf.mbauth, "Citadel");
  1856.         putMessage(&logBuf);  /* Now save message in this room*/
  1857.         noteRoom();
  1858.  
  1859.         }
  1860.       if (tmp != NULL) free(tmp);
  1861.  
  1862.       }
  1863.  
  1864.     }
  1865.   /*
  1866.   * visible()
  1867.   *
  1868.   * This converts given char to printable form if nonprinting.
  1869.   */
  1870.   char visible(AN_UNSIGNED c)
  1871.     {
  1872.     if (c==0xFF)  c = '$' ;   /* start-of-message in message.buf  */
  1873.     c       = c & 0x7F  ;   /* kill high bit otherwise    */
  1874.     if ( c < ' ') c = c + 'A' -1;   /* make all control chars letters   */
  1875.     if (c== 0x7F) c = '~' ;   /* catch DELETE too     */
  1876.     return (char)(c);
  1877.  
  1878.     }
  1879.   char *Menu = NULL;
  1880.   char **ValidMenuOpts;
  1881.   /*
  1882.   * GetMenuChar()
  1883.   *
  1884.   * This will get a character for a menu.
  1885.   */
  1886.   int GetMenuChar()
  1887.     {
  1888.     int c, i;
  1889.     c = toUpper(iChar());
  1890.     for (i = 0; ValidMenuOpts[i][0]; i++)
  1891.     if (c == ValidMenuOpts[i][0])
  1892.     break;
  1893.     if (!ValidMenuOpts[i][0])
  1894.       {
  1895.       if (!onLine() || (expert && c != '?'))
  1896.         {
  1897.         c = 0;
  1898.         mPrintf(" ?\n ");
  1899.  
  1900.         }
  1901.       else
  1902.         {
  1903.         c = '?';
  1904.         if (Menu != NULL) tutorial(Menu, TRUE);
  1905.  
  1906.         }
  1907.  
  1908.       }
  1909.     else mPrintf("%s ", ValidMenuOpts[i] + 1);
  1910.     return c;
  1911.  
  1912.     }
  1913.   /*
  1914.   * ExtraOption()
  1915.   *
  1916.   * This adds an option to a menu.
  1917.   */
  1918.   void ExtraOption(char *Opts[], char *NewOpt)
  1919.     {
  1920.     int i;
  1921.     for (i = 0; Opts[i][0] && Opts[i][0] != ' '; i++)
  1922.     ;
  1923.     if (!Opts[i][0])
  1924.     crashout("INTERNAL: No room for new option!");
  1925.     Opts[i] = NewOpt;
  1926.  
  1927.     }
  1928.   static char *MPtr;
  1929.   static int  MCount;
  1930.   /*
  1931.   * CmdMenuList()
  1932.   *
  1933.   * This reads in a command line, doing backspacing.
  1934.   */
  1935.   int CmdMenuList(char *Opts[], SListBase *Selects, char *HelpFile, char *buf,
  1936.   char moreYet, char OneMore)
  1937.     {
  1938.     int c, rover;
  1939.     char *cmd;
  1940.     void CopyBuf();
  1941.     do
  1942.       {
  1943.       c = toUpper(iChar());
  1944.       if (c == '\b')
  1945.         {
  1946.         mPrintf(" \b");
  1947.         if ((cmd = GetLast(Selects)) == NULL)
  1948.           {
  1949.           if (!OneMore) oChar(' ');
  1950.           return BACKED_OUT;
  1951.  
  1952.           }
  1953.         for (rover = 0; rover < strlen(cmd) - 1; rover++)
  1954.         if (cmd[rover] != '\b') mPrintf("\b \b");
  1955.         else oChar(' ');
  1956.         KillData(Selects, cmd);
  1957.         if (OneMore)
  1958.         mPrintf("\b \b");
  1959.  
  1960.         }
  1961.       else
  1962.         {
  1963.         for (rover = 0; Opts[rover][0] != 0; rover++)
  1964.         if (toUpper(Opts[rover][1]) == toupper(c))
  1965.         break;
  1966.         if (c == 0 || Opts[rover][0] == 0 ||
  1967.         SearchList(Selects, Opts[rover] + 1) != NULL)
  1968.           {
  1969.           if (!onLine() || (expert && c != '?'))
  1970.             {
  1971.             mPrintf(" ?\n ");
  1972.  
  1973.             }
  1974.           else if (HelpFile != NULL)
  1975.             {
  1976.             tutorial(HelpFile, TRUE);
  1977.  
  1978.             }
  1979.           else mPrintf(" ? (Type '?' for menu)\n \n"   );
  1980.           buf[0] = 1;   /* general error */
  1981.           return BAD_SELECT;
  1982.  
  1983.           }
  1984.         mPrintf("%s ", Opts[rover] + 2);
  1985.         AddData(Selects, Opts[rover] + 1, NULL, FALSE);
  1986.  
  1987.         }
  1988.  
  1989.       }
  1990.     while ((c == '\b' || Opts[rover][0] != TERM[0]) && moreYet);
  1991.     MPtr = buf;
  1992.     MCount = 0;
  1993.     RunList(Selects, CopyBuf);
  1994.     return GOOD_SELECT;
  1995.  
  1996.     }
  1997.   void CopyBuf(char *name)
  1998.     {
  1999.     MPtr[MCount++] = name[0];
  2000.     MPtr[MCount] = 0;
  2001.  
  2002.     }
  2003.   /*
  2004.   * FindSelect()
  2005.   *
  2006.   * This function will find an element of a command selection.
  2007.   */
  2008.   void *FindSelect(char *element, char *data)
  2009.     {
  2010.     if (element == data) return element;
  2011.     return NULL;
  2012.  
  2013.     }
  2014.